# LambdaParameterBinder.java

    package org.codefilarete.stalactite.sql.statement.binder;

    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.util.function.Function;

    import org.codefilarete.tool.function.SerializableThrowingFunction;

    /**
     * A {@link ParameterBinder} aimed at simplifying {@link ResultSet} and {@link PreparedStatement} access thanks to method references.
     *
     * @author Guillaume Mary
     */
    public class LambdaParameterBinder<T> implements ParameterBinder<T> {

    	private final ResultSetReader<T> resultSetReader;
    	private final PreparedStatementWriter<T> preparedStatementWriter;
    	private final ParameterBinder<?> delegate;

    	/**
    	 * Default basic constructor that delegates its mechanism to a given {@link ResultSetReader} and a {@link PreparedStatementWriter}
    	 *
    	 * @param resultSetReader a {@link ResultSetReader}
    	 * @param preparedStatementWriter a {@link PreparedStatementWriter}
    	 */
    	public LambdaParameterBinder(ResultSetReader<T> resultSetReader, PreparedStatementWriter<T> preparedStatementWriter) {
    		this.resultSetReader = resultSetReader;
    		this.preparedStatementWriter = preparedStatementWriter;
    		this.delegate = null;
    	}

    	/**
    	 * Composes a new {@link LambdaParameterBinder} from another one which is wrapped by 2 {@link Function}s for converting input and output values
    	 *
    	 * @param delegate the root {@link LambdaParameterBinder}
    	 * @param resultSetConverter the converter applied on the value read by the root binder
    	 * @param statementInputConverter the converter applied on the value passed to the root binder
    	 * @param <I> type of the root binder's value type, which is the Java type of the read and written value from/to the database
    	 */
    	public <I> LambdaParameterBinder(ParameterBinder<I> delegate,
    									 SerializableThrowingFunction<I, T, ? extends Throwable> resultSetConverter,
    									 SerializableThrowingFunction<T, I, ? extends Throwable> statementInputConverter) {
    		this.resultSetReader = delegate.thenApply(resultSetConverter);
    		this.preparedStatementWriter = delegate.preApply(statementInputConverter);
    		this.delegate = delegate;
    	}

    	@Override
    	public Class<T> getType() {
    		return resultSetReader.getType();
    	}

    	@Override
    	public <O> Class<O> getColumnType() {
    		return (Class<O>) (this.delegate == null ? getType() : delegate.getColumnType());
    	}

    	@Override
    	public T doGet(ResultSet resultSet, String columnName) {
    		return resultSetReader.get(resultSet, columnName);
    	}

    	@Override
    	public void set(PreparedStatement statement, int valueIndex, T value) throws SQLException {
    		preparedStatementWriter.set(statement, valueIndex, value);
    	}
    }